因為 elasticsearch 要打很多字,寫文苦手如我決定縮寫它,所以會用 ES 代稱。
auto-complete 除了自動補完之外,另外一個說法是『猜你想搜尋什麼』也就是從各種資訊中猜測使用者想要搜尋的內容;我們可以從幾個方向去『猜』使用者的想法,第一個是機率論,紀錄比較多的關鍵字,就代表越多人討論,也就有可能越多人想問;另外一個方向會是從使用者的『歷史搜尋』中猜測,曾經搜尋過的,可能會想再搜尋一次,或是與其相關的主題。
要實作出使用者的搜尋紀錄,需要有一個 db 或檔案紀錄使用者搜尋歷程,這部分目前並沒有在這系列的計畫中。因此『猜』的方向會是從現有紀錄中,找到最相關的資訊為主。
之前實作時的想法大概分成三個部分:
本篇先來重現一下第一個部分:補完沒有打完的單字(詞),假設是 cov。
想法是這樣的:
(1) 找出以這個沒有打完的單字為前綴(prefix)的 token,
(2) 根據某種規則排序這些 token 誰會在最前面。
上述的 (1) 蠻好解的,只要透過 prefix-query 篩選出包含以 cov 為前綴的 token 的文件就可以了:
GET /covid19_tweets/_search
{
"query": {
"prefix": {
"tweet": {
"value": "cov"
}
}
}
}
上述的 (2) 就有點困難,尤其是『根據某種規則排序』這件事;當時的解決方式是透過 significant_text
這個 aggregation 來完成。
先簡單說明一下 aggregation 是什麼,前面描述了很多搜尋的方法,但當搜尋出來的結果,想要進行彙整時,就會透過 aggregation 來處理;例如某個 query 搜尋出了 200 篇文章,這 200 篇文章我想要依據日期進行計算出每一天有幾篇文章,就可以透過 aggregation 來進行。
ES 提供很多 aggregation function 可以使用,significant_text 就是其中一個,他所執行的內容就是針對搜尋出來的文章,依據某種方式找出『重要的 token』,aggregation query 內容如下:
GET /covid19_tweets/_search
{
"query": {
"prefix": {
"tweet": {
"value": "cov"
}
}
},
"aggs": {
"find_the_whole_token": {
"significant_text": {
"field": "tweet",
"include": "cov.+",
"size": 2,
"min_doc_count": 100
}
}
},
"fields": ["aggregation"],
"_source": false
}
翻譯一下 aggs 內的語法:find_the_whole_token
是一個的 aggregation 名稱,這個 aggregation 要使用 significant_text
這個 ES 提供的 aggregation function,將其做用在 tweet
欄位,透過 significant_text 找出來的 token 要:
a. 符合 `cov.+` 這個 pattern(regex)
b. 至少在 100 筆資料(documents)中出現
並且請最多回傳兩個這樣的 token 給我;ES 給了以下的產出:
{
"took": 371,
"timed_out": false,
"_shards": {
"total": 1,
"successful": 1,
"skipped": 0,
"failed": 0
},
"hits": {
"total": {
"value": 10000,
"relation": "gte"
},
"max_score": 1,
"hits": [
...(略)...
]
},
"aggregations": {
"find_the_whole_token": {
"doc_count": 50000,
"bg_count": 354600,
"buckets": [
{
"key": "covid",
"doc_count": 32250,
"score": 1.64217,
"bg_count": 64500
},
{
"key": "covid19",
"doc_count": 16263,
"score": 0.82811196,
"bg_count": 32526
}
]
}
}
}
這樣的搜尋找出的兩個 token 是:covid
和 covid19
,看起來結果蠻符合預期的,但其實有一些不太理想的地方:
significant_text
效率沒有很好,這個搜尋花了一些時間,在一個搜尋的狀況下還沒有什麼影響,但當要進行 search as you type
時,使用者每打一個字就要等待一小段時間,會顯得相當緩慢,當使用者人數變多時也會對 ES 服務有很大的負擔。include
這個篩選機制來達到目的,並不是當初 significant_text
設計的使用方式。接下來幾篇會先照著之前的實作設計走完,把遇到覺得不太理想的地方都記錄下來,最後再來看看有沒有可以優化的地方。